
/*
 * Returns a free for dynamic slot assignment of the Online state. The states
 * are protected by the cpuhp_slot_states mutex and an empty slot is identified
 * by having no name assigned.
 */
static int cpuhp_reserve_state(enum cpuhp_state state)
{
    enum cpuhp_state i, end;
    struct cpuhp_step *step;

    switch (state)
    {
    case CPUHP_AP_ONLINE_DYN:
        step = cpuhp_get_step(CPUHP_AP_ONLINE_DYN);
        end = CPUHP_AP_ONLINE_DYN_END;
        break;
    case CPUHP_BP_PREPARE_DYN:
        step = cpuhp_get_step(CPUHP_BP_PREPARE_DYN);
        end = CPUHP_BP_PREPARE_DYN_END;
        break;
    default:
        return -EINVAL;
    }

    for (i = state; i <= end; i++, step++)
    {
        if (!step->name)
            return i;
    }
    WARN(1, "No more dynamic states available for CPU hotplug\n");
    return -ENOSPC;
}

static int cpuhp_store_callbacks(enum cpuhp_state state, const char *name,
                                 int (*startup)(unsigned int cpu),
                                 int (*teardown)(unsigned int cpu),
                                 bool multi_instance)
{
    /* (Un)Install the callbacks for further cpu hotplug operations */
    struct cpuhp_step *sp;
    int ret = 0;

    /*
     * If name is NULL, then the state gets removed.
     *
     * CPUHP_AP_ONLINE_DYN and CPUHP_BP_PREPARE_DYN are handed out on
     * the first allocation from these dynamic ranges, so the removal
     * would trigger a new allocation and clear the wrong (already
     * empty) state, leaving the callbacks of the to be cleared state
     * dangling, which causes wreckage on the next hotplug operation.
     */
    if (name && (state == CPUHP_AP_ONLINE_DYN ||
                 state == CPUHP_BP_PREPARE_DYN))
    {
        ret = cpuhp_reserve_state(state);
        if (ret < 0)
            return ret;
        state = ret;
    }
    sp = cpuhp_get_step(state);
    if (name && sp->name)
        return -EBUSY;

    sp->startup.single = startup;
    sp->teardown.single = teardown;
    sp->name = name;
    sp->multi_instance = multi_instance;
    INIT_HLIST_HEAD(&sp->list);

    return ret;
}

/*
 * Call the startup/teardown function for a step either on the AP or
 * on the current CPU.
 */
static int cpuhp_issue_call(int cpu, enum cpuhp_state state, bool bringup,
                            struct hlist_node *node)
{
    struct cpuhp_step *sp = cpuhp_get_step(state);
    int ret;

    /*
     * If there's nothing to do, we done.
     * Relies on the union for multi_instance.
     */
    if (cpuhp_step_empty(bringup, sp))
        return 0;
    /*
     * The non AP bound callbacks can fail on bringup. On teardown
     * e.g. module removal we crash for now.
     */
#ifdef CONFIG_SMP
    if (cpuhp_is_ap_state(state))
        ret = cpuhp_invoke_ap_callback(cpu, state, bringup, node);
    else
        ret = cpuhp_invoke_callback(cpu, state, bringup, node, NULL);
#else
    ret = cpuhp_invoke_callback(cpu, state, bringup, node, NULL);
#endif
    BUG_ON(ret && !bringup);

    return ret;
}

/*
 * Called from __cpuhp_setup_state on a recoverable failure.
 *
 * Note: The teardown callbacks for rollback are not allowed to fail!
 */
static void cpuhp_rollback_install(int failedcpu, enum cpuhp_state state,
                                   struct hlist_node *node)
{
}
